package com.openflag.bwicservice.service.impl;

import com.openflag.bwicservice.controller.BwicController;
import com.openflag.bwicservice.domain.*;
import com.openflag.bwicservice.domain.vo.BwicVO;
import com.openflag.bwicservice.exception.BwicOverdueException;
import com.openflag.bwicservice.exception.UserDidNotBidException;
import com.openflag.bwicservice.messagequeue.BwicProducer;
import com.openflag.bwicservice.repository.BidRepository;
import com.openflag.bwicservice.repository.BwicRepository;
import com.openflag.bwicservice.service.AuditService;
import com.openflag.bwicservice.service.BwicService;
import com.openflag.bwicservice.service.LoginService;
import lombok.extern.java.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class BwicServiceImpl implements BwicService {

    @Autowired
    private BwicRepository bwicRepository;
    @Autowired
    private BidRepository bidRepository;
    @Autowired
    private LoginService loginService;
    @Autowired
    private BwicProducer bwicProducer;
    @Autowired
    private AuditService auditService;
    @Autowired
    private RestTemplate restTemplate;

    Logger LOG = LoggerFactory.getLogger(BwicController.class);

    @Override
    public List<BwicVO> list(Long clientId) {
        // 这个方法会返回一个按照id字段降序排列的Bwic实体类列表,查询了所有的bwic形成列表。
        List<Bwic> bwics = bwicRepository.getAvailableBwic();

        Bid queryClient = new Bid();
        queryClient.setClientId(clientId);
        // 查询将返回所有具有与 queryClient 对象的 clientId 属性值相同的值
        List<Bid> bids = bidRepository.findAll(Example.of(queryClient));

        return populateList(bwics, bids);
    }

    @Override
    public List<BwicVO> mylist(Long clientId) {
        Bid queryClient = new Bid();
        queryClient.setClientId(clientId);
        List<Bid> bids = bidRepository.findAll(Example.of(queryClient));
        Set<Long> bwicIds = bids.stream().map(Bid::getBwicId).collect(Collectors.toSet());
        List<Bwic> bwics = bwicRepository.findAllById(bwicIds)
                .stream()
                .sorted(Comparator.comparing(Bwic::getId).reversed())
                .collect(Collectors.toList());

        return populateList(bwics, bids);
    }

    @Override
    public List<BwicVO> bid(BidRequest bidRequest) {
        // todo
        // bid value cannot less than max(current, initial value)

        Bwic queryBwic = new Bwic();
        queryBwic.setId(bidRequest.getBwicId());
        Optional<Bwic> optionalBwic = bwicRepository.findOne(Example.of(queryBwic));


        Bid queryBid = new Bid();
        queryBid.setBwicId(bidRequest.getBwicId());
        queryBid.setClientId(bidRequest.getClientId());
        Optional<Bid> optionalBid = bidRepository.findOne(Example.of(queryBid));
        if (optionalBid.isPresent()){
            if (bidRequest.getBidMarketValue()<optionalBwic.get().getMarketValue() || bidRequest.getBidMarketValue() < optionalBid.get().getBidMarketValue()){
                return null;
            }
        }else{
            if (bidRequest.getBidMarketValue()<optionalBwic.get().getMarketValue()){
                return null;
            }
        }


        Bid myBid = null;
        // 之前参与过的bid，所以bid数据库可以找到，直接修改就可以了。
        if(optionalBid.isPresent()) {
            myBid = optionalBid.get();
            myBid.setBidMarketValue(bidRequest.getBidMarketValue());
            myBid.setBidTime(LocalDateTime.now());
        } else {
            myBid = new Bid();
            myBid.setBwicId(bidRequest.getBwicId());
            myBid.setClientId(bidRequest.getClientId());
            myBid.setBidMarketValue(bidRequest.getBidMarketValue());
            myBid.setBidTime(LocalDateTime.now());
        }
        bidRepository.save(myBid);

        // todo
        // bid meeesage
//        bwicProducer.sendBid(bidRequest);
        return list(bidRequest.getClientId());
    }

    @Override
    public List<BwicVO> cancel(CancelRequest cancelRequest) throws BwicOverdueException, UserDidNotBidException {
        Long bwicId = cancelRequest.getBwicId();
        Optional<Bwic> bwic = bwicRepository.findById(bwicId);
        if(bwic.isPresent() && bwic.get().getDueDate().isBefore(LocalDate.now())) {
            throw new BwicOverdueException();
        }

        Bid queryBid = new Bid();
        queryBid.setBwicId(cancelRequest.getBwicId());
        queryBid.setClientId(cancelRequest.getClientId());
        Optional<Bid> optionalBid = bidRepository.findOne(Example.of(queryBid));
        if(optionalBid.isPresent()) {
            bidRepository.deleteById(optionalBid.get().getId());


            // todo

        } else {
            // user didn't bid exception
            throw new UserDidNotBidException();
        }

        return list(cancelRequest.getClientId());
    }

    @Override
    public List<Client> top3list(Long bwicId){
        Bid queryBid = new Bid();
        queryBid.setBwicId(bwicId);

        List<Bid> bidList = bidRepository.findAll(Example.of(queryBid));
        Integer length = bidList.size();

        bidList.sort(Comparator.comparing(Bid::getBidMarketValue).reversed());

        Integer i = 0;
        List<Client> clientList = new ArrayList<>();
        for (Bid bid : bidList){
            Long ClientId = bid.getClientId();
            Client client = loginService.getClient(ClientId);
            clientList.add(client);
            i++;
            if (i==3||i==length){
                return clientList;
            }





        }
        return clientList;




    }


    // 一个函数，输入需要查询的bwic列表，和client已经参与的bid竞价，返回给你client参与的在bwic列表里的bwicVO条目信息，显示状态。
    private List<BwicVO> populateList(List<Bwic> bwics, List<Bid> bids) {
        List<BwicVO> list = new ArrayList<>();

        // 得到bids列表映射的<BwicId,Bid>，这里面包含了与bid有关系的bwic。
        Map<Long, Bid> bwicBidMap = bids.stream().collect(Collectors.toMap(Bid::getBwicId, b -> b, (oldVal, newVal) -> newVal));

//        上列代码等价于：
//        Map<Long, Bid> bwicBidMap = new HashMap<>();
//        for (Bid b : bids) {
//            bwicBidMap.put(b.getBwicId(), b);
//        }



        for(Bwic bwic : bwics) {
            // 单独提出一个bwic，将其状态赋值给一个bwic。VO
            BwicVO bwicVO = new BwicVO();
            bwicVO.setBwicId(bwic.getId());
            bwicVO.setCusip(bwic.getCusip());
            bwicVO.setPosition(bwic.getPosition());
            bwicVO.setPrice(bwic.getPrice());
            bwicVO.setDueDate(bwic.getDueDate());
            bwicVO.setMarketValue(bwic.getMarketValue());
            bwicVO.setOverDue(bwic.getDueDate().isBefore(LocalDate.now()));

            Long bwicId = bwic.getId();
            // 获取bwic的count数，赋值给BIWCVO
            // todo:改auditservice
            Integer count = auditService.getCount(bwicId);
            if(count != null) {
                bwicVO.setTotalBids(count);
            }


            if(bwicBidMap.containsKey(bwicId)) {
                // bwiclist里的bwicId如果在这个映射列表里，
                // 用他的Id提取出对应的竞标记录。
                Bid bid = bwicBidMap.get(bwicId);
                // 这个状态条目信息的bidMarketValue是client的出价
                bwicVO.setBidMarketValue(bid.getBidMarketValue());

                Long clientId = bid.getClientId();
                String myRank = auditService.getMyRank(bwicId, clientId);
                if(!"null".equalsIgnoreCase(myRank) && myRank != null) {
                    bwicVO.setBidRank(myRank);
                }
            }

            if(bwicVO.getOverDue()) {
                // String winner;
                // get winner client id from mysql
                // select client_id from bid where bwic_id = bwicId and bid_market_value = max(value)
                Long winnerClientId = bidRepository.getWinnerIdByBwicId(bwicId);

                LOG.info("WinnerClientId ==> {}",winnerClientId);
                if(winnerClientId != null) {



                    Client client = loginService.getClient(winnerClientId);
                    // 这里报错null，因为client用户写的太少了，winner查不到，实际应用中不会报错。
                    LOG.info("WinnerClient ==> {}",client);
                    String clientName = client.getName();

                    bwicVO.setWinner(clientName);
                }
            }

            list.add(bwicVO);
        }

        return list;
    }

}
